package csm.common;

import java.sql.Time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * 날짜관련 함수를 제공하는 클래스
 * 
 * @author seungmun
 * @since 2006.1.18.
 */
public final class DateUtil
{
	private static String getTZOffset()
	{
		return getTZOffset(TimeZone.getDefault().getID());
	}

	private static String getTZOffset(String timeZone)
	{
		String offset, tmp;
		TimeZone tz = TimeZone.getTimeZone(timeZone);
		
		if (tz == null) return " ";
		
		// attach timezone offset(ex: +0900)
		tmp = new Integer(tz.getRawOffset() / 36000).toString();
		
		if (tz.getRawOffset() / 36000 > 0)
		{
			if (tmp.length() == 3) tmp = "0" + tmp;
			offset = " +" + tmp;
		}
		else if (tz.getRawOffset() / 36000 < 0)
		{
			if (tmp.length() <= 4) tmp = tmp.substring(0, 1) + (tmp.length() == 4 ? "0" : "00") + tmp.substring(1);
			offset = " " + tmp;
		}
		else
			offset = "";
		
		return offset;
	}

	/**
	 * RFC822 Format의 Date 형태로 리턴한다.
	 * 
	 * @return String
	 */
	public static String rfc822_date()
	{
		Date dt = new Date();
		String date, offset;
		SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.US);
		
		offset = getTZOffset();
		date = df.format(dt) + (offset.length() > 0 ? offset : " ");
		
		// attach TimeZone
		df.applyPattern("(z)");
		date += df.format(dt);
		
		return date;
	}

	/**
	 * yyyy-MM-dd Format의 Date 형태로 리턴한다.
	 * 
	 * @return 현재날짜를 yyyy-MM-dd 형태로 리턴
	 */
	public static String getDate()
	{
		Date dt = new Date();
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
		return df.format(dt);
	}

	/**
	 * 지정한 yyyyMMdd값을 사용하여 Calendar객체로 반환합니다.
	 * 
	 * @param baseDate
	 * @return
	 */
	public static Calendar getCalendar(String baseDate)
	{
		try
		{
			if (baseDate == null || baseDate.length() != 8) return Calendar.getInstance();

			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
			sdf.parse(baseDate);
			return sdf.getCalendar();
		}
		catch (Exception e)
		{
			return Calendar.getInstance();
		}
	}

	/**
	 * format에 지정된 형태로 날짜를 리턴한다.
	 * 
	 * @param format
	 *            yyyy:년도, MM:월, dd:일, HH:24시간, mm:분, ss:초, S:millisec
	 * @return 현재날짜를 지정된 형태로 리턴
	 */
	public static String getDate(String format)
	{
		Date dt = new Date();
		SimpleDateFormat df = new SimpleDateFormat(format, Locale.US);
		return df.format(dt);
	}

	/**
	 * format에 지정된 형태로 날짜를 리턴한다.
	 * 
	 * @param format
	 *            yyyy:년도, MM:월, dd:일, HH:24시간, mm:분, ss:초, S:millisec
	 * @return 현재날짜를 지정된 형태로 리턴
	 */
	public static String getDate(Calendar c, String format)
	{
		Date dt = new Date(c.getTimeInMillis());
		SimpleDateFormat df = new SimpleDateFormat(format, Locale.KOREA);
		return df.format(dt);
	}

	/**
	 * 현재 날짜를 yyyyMMdd로 리턴한다.
	 * 
	 * @return String yyyyMMdd
	 */
	public static String getCurrentDate()
	{
		return getDate("yyyyMMdd");
	}

	/**
	 * 현재날짜에서 주어진 일자만큼 계산한후 yyyyMMdd포맷으로 리턴한다.
	 * 
	 * @param day
	 *            add할 일자
	 * @return yyyyMMdd String
	 */
	public static String addDate(int day)
	{
		return addDate(day, "yyyyMMdd");
	}

	/**
	 * 현재날짜에서 주어진 일자만큼 계산한후 yyyyMMdd포맷으로 리턴한다.
	 * 
	 * @param day
	 *            add할 일자
	 * @return yyyyMMdd String
	 */
	public static String addDate(int day, String format)
	{
		Calendar calendar = Calendar.getInstance();
		
		calendar.add(Calendar.DATE, day);
		
		long t = calendar.getTimeInMillis();
		
		SimpleDateFormat sdf = new SimpleDateFormat();
		sdf.applyPattern(format);
		
		Time ti = new Time(t);
		return sdf.format(ti);
	}

	/**
	 * 현재날짜에서 주어진 일자만큼 계산한후 yyyyMMdd포맷으로 리턴한다.
	 * 
	 * @param fromDate
	 *            기준일자(yyyyMMdd)
	 * @param day
	 *            add할 일자
	 * @return yyyyMMdd String
	 */
	public static String addDate(String fromDate, int day)
	{
		try
		{
			Calendar calendar = Calendar.getInstance();
			
			int year, month, date;
			
			if (fromDate == null || fromDate.length() != 8) return fromDate;
			
			year = Integer.parseInt(fromDate.substring(0, 4));
			month = Integer.parseInt(fromDate.substring(4, 6)) - 1;
			date = Integer.parseInt(fromDate.substring(6));
			
			calendar.set(year, month, date);
			calendar.add(Calendar.DATE, day);
			
			long t = calendar.getTimeInMillis();
			
			SimpleDateFormat sdf = new SimpleDateFormat();
			sdf.applyPattern("yyyyMMdd");
			Time ti = new Time(t);
			
			return sdf.format(ti);
		}
		catch (Exception e)
		{
			return fromDate;
		}
	}

	/**
	 * baseDate에 지정한 월을 더한 날짜를 반환합니다.
	 * 
	 * @param baseDate
	 *            : yyyymmdd
	 * @param monthValue
	 * @return yyyymmdd
	 */
	public static String addMonth(String baseDate, int monthValue)
	{
		try
		{
			if (baseDate == null || baseDate.length() != 8) return baseDate;

			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
			sdf.parse(baseDate);

			Calendar c = sdf.getCalendar();
			c.add(Calendar.MONTH, monthValue);
			
			return sdf.format(c.getTime());
		}
		catch (Exception e)
		{
			return baseDate;
		}
	}

	/**
	 * 현재날짜에서 주어진 달수만큼 계산한후 yyyyMMdd 포맷으로 리턴한다.
	 * 
	 * @param month
	 *            add할 달수
	 * @return yyyyMMdd String
	 */
	public static String addMonth(int month)
	{
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, month);
		
		long t = calendar.getTimeInMillis();
		
		SimpleDateFormat sdf = new SimpleDateFormat();
		sdf.applyPattern("yyyyMMdd");
		
		Time ti = new Time(t);
		
		return sdf.format(ti);
	}

	/**
	 * 현재날짜에서 주어진 달수만큼 계산한후 yyyyMMdd 포맷으로 리턴한다.
	 * 
	 * @param month
	 *            add할 달수
	 * @return yyyyMMdd String
	 */
	public static String addYear(int year)
	{
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.YEAR, year);
		
		long t = calendar.getTimeInMillis();
		
		SimpleDateFormat sdf = new SimpleDateFormat();
		sdf.applyPattern("yyyyMMdd");
		
		Time ti = new Time(t);
		
		return sdf.format(ti);
	}

	/**
	 * 월의 마지막 날을 구한다.
	 * 
	 * @param year
	 * @param month
	 * @return
	 */
	public static int getLastDate(int year, int month)
	{
		Calendar now = Calendar.getInstance();
		now.set(year, month - 1, 1);
		return now.getActualMaximum(Calendar.DATE);
	}

	public static String normalizeDate(String date)
	{
		try
		{
			if (date == null || date.length() == 0) return date;
			
			char endChar = date.charAt(date.length() - 1);
			
			if (!Character.isDigit(endChar) && endChar != '?') return date;
			
			StringBuffer sb = new StringBuffer();
			
			for (int i = 0; i < date.length(); i++)
			{
				char c;
				if ((Character.isDigit(c = date.charAt(i)) && c != '-') || c == '?')
				{
					sb.append(c);
				}
			}

			// 길이가 6, 8인 경우에
			if (sb.length() == 6 || sb.length() == 8)
			{
				String s = sb.toString();
				if (!s.endsWith("00") && !s.endsWith("??"))
				{
					if (s.length() == 6) return sb.substring(0, 4) + "-" + sb.substring(4, 6);
					if (s.length() == 8) return sb.substring(0, 4) + "-" + sb.substring(4, 6) + "-" + sb.substring(6, 8);
				}
				
				if (sb.length() == 6)
				{
					if (sb.substring(0, 4).equals("0000") || sb.substring(0, 4).equals("????")) return "";
					return sb.substring(0, 4);
				}
				
				if (sb.length() == 8)
				{
					if (s.endsWith("0000") || s.endsWith("????"))
					{
						if (sb.substring(0, 4).equals("0000") || sb.substring(0, 4).equals("????")) return "";
						return sb.substring(0, 4);
					}
					
					if (s.endsWith("00") || s.endsWith("??")) return sb.substring(0, 4) + "-" + sb.substring(4, 6);
					return sb.substring(0, 4) + "-" + sb.substring(4, 6) + "-" + sb.substring(6, 8);
				}
				
				return sb.toString();
			}
			else if (sb.length() == 4)
			{
				String tmp1 = sb.toString();
				
				if (tmp1.equals("0000") || tmp1.equals("????"))
					return "";
				else
					return sb.toString();
			}
			else
			{
				return date;
			}
		}
		catch (Exception e)
		{
			return date;
		}
	}

	/**
	 * 날짜에서 날짜를 뺀 값 즉 기간을 반환한다.
	 * 
	 * @param date1
	 *            기준일
	 * @param date2
	 *            기준일에서 부터 뺄 날짜
	 * @param format
	 *            {@link java.text.SimpleDateFormat}의 날짜 포맷팅 문자열
	 * @return long 두 날짜 사이의 기간, 같은 날짜인 경우엔 0이된다.
	 */
	public static long distance(String date1, String date2, String format) throws ParseException
	{
		return distance(DateUtil.date(format, date1), DateUtil.date(format, date2));
	}

	public static int distance(java.util.Date date, java.util.Date minusDate)
	{
		long l = date.getTime() - minusDate.getTime();
		return (int) (l / 0x5265c00L);
	}

	/**
	 * 문자열에 날짜포맷팅을 적용시켜서 날자형으로 변환한다.
	 * 
	 * @param format
	 *            {@link java.text.SimpleDateFormat}의 날짜 포맷팅 문자열
	 * @param value
	 *            변환할 문자열 형태의 날짜
	 * @return 변환된 날짜를 반환한다.
	 * @exception ParseException
	 *                날짜 변환이 실패한 경우에 발생한다.
	 */
	public static Date date(String format, String value) throws ParseException
	{
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		return sdf.parse(value);
	}

	/**
	 * 시간 문자열을 초로 변경한다. (hh:mm:ss 또는 mm:ss 또는 ss 형식)
	 * 
	 * @param time
	 * @return
	 */
	public static int getTimeToSecond(String time)
	{
		if (time == null || time.length() == 0) return 0;

		String[] timeSplit = time.split(":");
		int second = 0;
		
		if (timeSplit.length == 1)
		{
			second += Integer.parseInt(timeSplit[0]);
			return second;
		}
		
		if (timeSplit.length == 2)
		{
			second += (Integer.parseInt(timeSplit[0]) * 60);
			second += Integer.parseInt(timeSplit[1]);
			return second;
		}

		if (timeSplit.length == 3)
		{
			second += (Integer.parseInt(timeSplit[0]) * 60 * 60);
			second += (Integer.parseInt(timeSplit[1]) * 60);
			second += Integer.parseInt(timeSplit[2]);
			return second;
		}
		
		if (timeSplit.length == 4)
		{
			second += (Integer.parseInt(timeSplit[0]) * 60 * 60);
			second += (Integer.parseInt(timeSplit[1]) * 60);
			second += Integer.parseInt(timeSplit[2]);
			return second;
		}
		
		return 0;
	}

	/**
	 * 오늘의 요일을 구한다.
	 * 
	 * @param c
	 *            Calendar
	 * @return 문자 (예: 금요일)
	 */
	public static String getDayOfWeek(Calendar c)
	{
		switch (c.get(Calendar.DAY_OF_WEEK))
		{
			case 1:
				return "일요일";
			case 2:
				return "월요일";
			case 3:
				return "화요일";
			case 4:
				return "수요일";
			case 5:
				return "목요일";
			case 6:
				return "금요일";
			case 7:
				return "토요일";
			default:
				return "토요일";
		}
	}

	/**
	 * 오늘의 요일을 구한다.
	 * 
	 * @return 문자 (예: 금요일)
	 */
	public static String getDayOfWeek()
	{
		return getDayOfWeek(Calendar.getInstance());
	}

	public static void main(String[] args)
	{
		Calendar c = Calendar.getInstance();
		c.set(2007, 5, 19);
		System.out.println(getDate(c, "yyyy년 MM월 dd일"));
		System.out.println(getDayOfWeek(c));
		
		System.out.println(DateUtil.rfc822_date());
	}
}
